package cn.monkey.family.server.domain.uc.auth.session;

import cn.monkey.family.data.uc.auth.UserSession;
import cn.monkey.family.data.http.HttpHeaderConstants;
import com.google.common.base.Strings;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.client.HttpClientErrorException;

import java.io.IOException;
import java.util.function.Consumer;
import java.util.function.Function;

public class UserSessionFilter implements Filter {

    private static final Logger log = LoggerFactory.getLogger(UserSessionFilter.class);

    private final Function<String, UserSession> userSessionFindFunction;
    private final Consumer<UserSession> afterUserSessionFind;
    private final Runnable afterFilter;

    public UserSessionFilter(Function<String, UserSession> userSessionFindFunction,
                             Consumer<UserSession> afterUserSessionFind,
                             Runnable afterFilter) {
        this.userSessionFindFunction = userSessionFindFunction;
        this.afterUserSessionFind = afterUserSessionFind;
        this.afterFilter = afterFilter;
    }


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (servletRequest instanceof HttpServletRequest httpServletRequest && servletResponse instanceof HttpServletResponse httpServletResponse) {
            this.doFilter0(httpServletRequest, httpServletResponse, filterChain);
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

    private void doFilter0(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String authorizationKey = httpServletRequest.getHeader(HttpHeaderConstants.AUTHORIZATION_KEY);
        if (Strings.isNullOrEmpty(authorizationKey)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }
        UserSession userSession;
        try {
            userSession = this.userSessionFindFunction.apply(authorizationKey);
        } catch (Exception e) {
            log.error("invalid user session key: {}", authorizationKey);
            throw HttpClientErrorException.create(HttpStatus.UNAUTHORIZED, "BAD_AUTHORIZATION_KEY", null, null, null);
        }
        if (userSession == null) {
            log.error("invalid user session key: {} for null user session", authorizationKey);
            throw HttpClientErrorException.create(HttpStatus.UNAUTHORIZED, "BAD_AUTHORIZATION_KEY", null, null, null);
        }
        httpServletRequest.setAttribute(UserSession.KEY, userSession);
        this.afterUserSessionFind.accept(userSession);
        filterChain.doFilter(httpServletRequest, httpServletResponse);
        this.afterFilter.run();
    }
}
